function move(ele, options, fn = () => {}) {
  let count = 0
  for (let key in options) {
    count++
    if (key === 'opacity') options[key] *= 100
    const timer = setInterval(() => {
      let current
      if (key === 'opacity') {
        current = window.getComputedStyle(ele)[key] * 100
      } else {
        current = parseInt(window.getComputedStyle(ele)[key])
      }
      let distance = (options[key] - current) / 10
      distance = distance > 0 ? Math.ceil(distance) : Math.floor(distance)

      if (current === options[key]) {
        clearInterval(timer)
        count--
        if (!count) fn()
      } else {
        if (key === 'opacity') {
          ele.style[key] = (current + distance) / 100
        } else {
          ele.style[key] = current + distance + 'px'
        }
      }
    }, 20)
  }
}

// 对象转换成 查询字符串
function querystringify(obj) {
  let str = ''
  for (let k in obj) {
    str += k + '=' + obj[k] + '&'
  }
  // str === 'name=jack&age=18&'
  return str.slice(0, -1)
}


function ajax(options = {}) {
  if (!options.url) {
    throw new Error('url 为必填选项 ! ^_^ ')
  }
  if (!(options.method === undefined || /^(get|post)$/i.test(options.method))) {
    throw new Error('method 目前只接受 POST 和 GET 请求方式 O(∩_∩)O哈哈~')
  }
  if (!(options.async === undefined || typeof options.async === 'boolean')) {
    throw new Error('async 需要传递一个 布尔值(boolean) 类型 (#^.^#)')
  }
  if (!(options.data === undefined || typeof options.data === 'string')) {
    throw new Error('data 需要传递一个 字符串(string) 类型 o(*￣︶￣*)o')
  }
  if (!(options.headers === undefined || Object.prototype.toString.call(options.headers) === '[object Object]')) {
    throw new Error('headers 需要传递一个 对象(Object) 数据类型 o(*￣︶￣*)o')
  }
  if (!(options.dataType === undefined || /^(string|json)$/i.test(options.dataType))) {
    throw new Error('目前 dataType 只接受 "string" 或者 "json" 选项 (*^▽^*)')
  }
  if (!(options.success === undefined || typeof options.success === 'function')) {
    throw new Error('success 需要传递一个 函数(function) 类型 hh')
  }

  const _default = {
    url: options.url,
    method: options.method || 'GET',
    async: options.async ?? true,
    data: options.data || '',
    headers: { 'content-type': 'application/x-www-form-urlencoded', ...options.headers },
    dataType: options.dataType || 'string',
    success: options.success || function () {}
  }
  
  if (/^get$/i.test(_default.method) && _default.data) {
    _default.url += '?' + _default.data
  }

  
  const xhr = new XMLHttpRequest()
  xhr.open(_default.method, _default.url, _default.async)
  xhr.onload = function () {
    let res = xhr.responseText
    if (_default.dataType === 'json') {
      res = JSON.parse(xhr.responseText)
    }
    _default.success(res)
  }

  for (let k in _default.headers) {
    xhr.setRequestHeader(k, _default.headers[k])
  }

  if (/^get$/i.test(_default.method)) {
    xhr.send()
  } else {
    xhr.send(_default.data)
  }
}

function pAjax(options) {
  const p = new Promise((resolve, reject) => {
    ajax({
      url: options.url,
      data: options.data,
      async: options.async,
      method: options.method,
      headers: options.headers,
      dataType: options.dataType,
      success (res) {
        resolve(res)
      }
    })
  })

  return p
}
